package edu.ua.bmr.claxml;

import java.net.URL;
import java.util.HashMap;
import java.util.Stack;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import android.util.Log;


/**
 * Constructs the data set that results from parsing a ClaXML document.
 * For use with SAX.
 * @author pmkilgo
 */
public class ClaXMLHandler extends DefaultHandler {
	/** Android tag for debugging */
	private static String TAG = "ClaXMLHandler";

	/** We will construct this data set */
	private ClaXMLSet mDataSet = new ClaXMLSet();
	
	/** The current row we are working on */
	private HashMap<String,String> mCurrentRow;
	
	/** A temporary location to store our collected data */
	private String mCollectedData;
	
	/** We will use a stack to represent the hierarchy of the document primarily for
	 * error checking.
	 */
	private Stack<String> mTagStack = new Stack<String>();

	/**
	 * Retrieves the data set constructed after SAX makes a pass through the XML.
	 * @return the data set for a given XML document.
	 */
	public ClaXMLSet getParsedData() {
		return this.mDataSet;
	}

	/**
	 * Called at the start of a new XML document.
	 */
	@Override
	public void startDocument() throws SAXException {
		mDataSet = new ClaXMLSet();
		mCollectedData = null;
		mTagStack.clear();
	}

	/**
	 * Called at the end of an XML document.
	 */
	@Override
	public void endDocument() throws SAXException {
		if(mTagStack.size() > 0) {
			mDataSet.setInvalid(true);
		}
	}

	/**
	 * Gets be called on opening tags like: <tag> Can provide attribute(s), when
	 * xml was like: <tag attribute="attributeValue">
	 */
	@Override
	public void startElement(String URI, String tagName, String foo, Attributes atts) throws SAXException {
		int level = mTagStack.size();
		mTagStack.push(tagName.toLowerCase());
		
		
		switch(level) {
		case 0:
			// We've just entered the top level which should mark
			// the response type.
			mDataSet.setResponseType( tagName );
			break;
			
		case 1:
			// We've entered a second-level which should mark
			// an object type. Allocate a new row.
			mDataSet.setObjectType(tagName.toLowerCase());
			mCurrentRow = new HashMap<String,String>();
			break;
			
		case 2:
			// This should be an attribute tag.
			// Do nothing for now.
			break;
			
		default:
			// This doesn't make sense.
			mDataSet.setInvalid(true);
			break;
		}
	}

	/**
	 * Gets be called on closing tags like: </tag>
	 */
	@Override
	public void endElement(String URI, String tagName, String foo)
			throws SAXException {
		
		if(mTagStack.size() == 0) {
			// This can't be valid if the stack is zero.
			// It would mean we never pushed something on in startElement.
			
			Log.e(TAG, "Ended tag with no beginning tag");
			mDataSet.setInvalid(true);
			return;
		}
		
		int level = mTagStack.size();
		
		tagName = tagName.toLowerCase();
		
		// Grab the opening tag.
		String lastTag = mTagStack.pop();
		
		if(lastTag.compareTo(tagName) != 0) {
			// Encapsulating tags don't match up!
			
			Log.e(TAG, "'" + tagName + "' cannot close '" + lastTag + "'");
			mDataSet.setInvalid(true);
			return;
		}
		
		// Tags match up. Now we can see if we need to do anything
		// to the data set.
		switch(level) {
		
		case 2:
			// We should be closing an object-type tag.
			// Add the current row to the result.
			mDataSet.addObject(mCurrentRow);
			mCurrentRow = null;
			break;
		
		case 3:
			// We should be closing a data field tag. If we have collected data,
			// add it to to the current row.
			
			if(mCollectedData != null) {
				mCurrentRow.put(tagName, mCollectedData);
				mCollectedData = null;
			}
			break;
		}
		
	}

	/**
	 * Gets called on the following structure: <tag>characters</tag>
	 */
	@Override
	public void characters(char ch[], int start, int length) {
		String data = new String(ch, start, length);
		data = data.trim();
		
		if(data.length() > 0) {
			// If there's anything but whitespace, we should have some good stuff!
			mCollectedData = data;
		}
	}
}
